package servidor.controle;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;

import servidor.bancoServidor.Log;
import servidor.modelo.Cliente;
import servidor.modelo.Conta;
import servidor.modelo.Extrato;
import servidor.modelo.TipoConta;
import servidor.modelo.TipoEstilo;

public class ContaControlador {

	public static void cria(Cliente cliente, TipoConta tipoDeConta, int reais,
			int centavos, TipoEstilo estiloDeConta) throws SQLException {
		Sistema sistema = Sistema.getUnicaInstancia();
		Conta conta;
		conta = sistema.getEntityManager().create(Conta.class);
		conta.setCliente(cliente);
		conta.setReais(0);
		conta.setCentavos(0);
		conta.setEstiloDeConta(estiloDeConta);
		conta.setTipoDeConta(tipoDeConta);
		conta.save();
		depositar(reais, centavos, conta);
	}

	public static void cria(Cliente cliente, TipoConta tipoDeConta, int reais,
			int centavos) throws SQLException {
		TipoEstilo estiloDeConta;
		Sistema sistema = Sistema.getUnicaInstancia();
		if (reais <= sistema.getValorMinimo()) {
			estiloDeConta = TipoEstilo.SIMPLES;
		} else {
			estiloDeConta = TipoEstilo.ESPECIAL;
		}
		cria(cliente, tipoDeConta, reais, centavos, estiloDeConta);
	}

	public static void atualiza(int id, Cliente cliente, TipoConta tipoDeConta,
			TipoEstilo estiloDeConta) throws SQLException {
		Conta conta = get(id);
		// passando null você quer dizer que não é necessário atualizar esse
		// valor da entidade
		if (cliente != null)
			conta.setCliente(cliente);
		if (estiloDeConta != null)
			conta.setEstiloDeConta(estiloDeConta);
		if (tipoDeConta != null)
			conta.setTipoDeConta(tipoDeConta);
		conta.save();
	}

	public static void depositar(int reais, int centavos, Conta conta) {
		credito(reais, centavos, conta, "Deposito");
		cobraImpostos(reais, centavos, conta);
	}

	public static void jurosPoupanca(double juros, Conta conta) {
		int dinheiro = conta.getReais() * 100 + conta.getCentavos();
		dinheiro = (int) (dinheiro * juros);
		int reais = dinheiro / 100;
		int centavos = dinheiro % 100;
		credito(reais, centavos, conta, "Juros Credito");
	}

	public static void jurosCorrente(double juros, Conta conta) {
		int dinheiro = conta.getReais() * 100 + conta.getCentavos();
		dinheiro = (int) (dinheiro * juros);
		int reais = dinheiro / 100;
		int centavos = dinheiro % 100;
		retirada(-reais, -centavos, conta, "Juros Debito");
	}

	private static void credito(int reais, int centavos, Conta conta,
			String operacao) {
		Dinheiro.add(conta, reais, centavos);
		conta.save();
		// Cria informação de extrato
		try {
			ExtratoControlador.cria(conta, operacao, String.format("+%d,%02d",
					reais, centavos), Log.DateNow());
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void depositar(int reais, int centavos, int contaId) {
		depositar(reais, centavos, get(contaId));
	}

	private static void retirada(int reais, int centavos, Conta conta,
			String operacao) {
		Dinheiro.sub(conta, reais, centavos);
		conta.save();
		// Cria informação de extrato
		try {
			ExtratoControlador.cria(conta, operacao, String.format("-%d,%02d",
					reais, centavos), Log.DateNow());
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void sacar(int reais, int centavos, Conta conta) {
		Sistema sistema = Sistema.getUnicaInstancia();
		if (conta.getTipoDeConta() == TipoConta.POUPANCA) {
			if (conta.getEstiloDeConta() == TipoEstilo.SIMPLES) {
				taxaMovimentacao(sistema.getMovimentacaoSimplesReais(), sistema
						.getMovimentacaoSimplesCentavos(), conta);
			} else {
				taxaMovimentacao(sistema.getMovimentacaoSimplesReais(), sistema
						.getMovimentacaoSimplesCentavos(), conta);
			}
		}
		retirada(reais, centavos, conta, "Retirada");
		cobraImpostos(reais, centavos, conta);
	}

	private static void cobraImpostos(int reais, int centavos, Conta conta) {
		Sistema sistema = Sistema.getUnicaInstancia();
		int dinheiro = reais * 100 + centavos;
		dinheiro = (int) (dinheiro * sistema.getImpostos());
		reais = dinheiro / 100;
		centavos = dinheiro % 100;
		retirada(reais, centavos, conta, "Imposto sobre movimentacao");
	}

	private static void taxaMovimentacao(int reais, int centavos, Conta conta) {
		retirada(reais, centavos, conta, "Taxa de movimentacao");
	}

	public static void manutencao(int reais, int centavos, Conta conta) {
		retirada(reais, centavos, conta, "Taxa de manutencao");
	}

	public static void sacar(int reais, int centavos, int contaId) {
		sacar(reais, centavos, get(contaId));
	}

	public static void transferir(int reais, int centavos, Conta contaOrigem,
			Conta contaDestino) {
		ContaControlador.depositar(reais, centavos, contaDestino);
		ContaControlador.sacar(reais, centavos, contaOrigem);
	}

	public static void transferir(int reais, int centavos, int contaOrigemId,
			int contaDestinoId) {
		transferir(reais, centavos, get(contaOrigemId), get(contaDestinoId));
	}

	public static Conta get(int id) {
		Sistema sistema = Sistema.getUnicaInstancia();
		return sistema.getEntityManager().get(Conta.class, id);
	}

	public static ArrayList<Conta> getCorrente(int clienteId) {
		Sistema sistema = Sistema.getUnicaInstancia();
		Conta[] contas;
		try {
			contas = sistema.getEntityManager().find(Conta.class,
					"clienteID=? and tipoDeConta=0", clienteId);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			contas = new Conta[0];
			e.printStackTrace();
		}
		if (contas.length == 0) {
			return null;
		}
		ArrayList<Conta> contasArray = new ArrayList<Conta>();
		sistema.fromArrayToArrayList(contas, contasArray);
		return contasArray;
	}

	public static boolean exists(int clienteId) {
		Sistema sistema = Sistema.getUnicaInstancia();
		Conta[] contas;
		try {
			contas = sistema.getEntityManager().find(Conta.class,
					"clienteID=?", clienteId);
		} catch (SQLException e) {
			contas = new Conta[0];
			e.printStackTrace();
		}
		if (contas.length == 0) {
			return false;
		}
		return true;
	}

	public static boolean contaExists(int id) {
		Sistema sistema = Sistema.getUnicaInstancia();
		Conta[] contas;
		try {
			contas = sistema.getEntityManager().find(Conta.class, "id = ?", id);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			contas = new Conta[0];
		}
		if (contas.length == 0) { // não existe
			return false;
		}
		return true;
	}

	public static ArrayList<Conta> getPoupanca(int clienteId) {
		Sistema sistema = Sistema.getUnicaInstancia();
		Conta[] contas;
		try {
			contas = sistema.getEntityManager().find(Conta.class,
					"clienteID=? and tipoDeConta=1", clienteId);
		} catch (SQLException e) {
			contas = new Conta[0];
			e.printStackTrace();
		}
		if (contas.length == 0) {
			return null;
		}
		ArrayList<Conta> contasArray = new ArrayList<Conta>();
		sistema.fromArrayToArrayList(contas, contasArray);
		return contasArray;
	}

	public static ArrayList<Conta> getAll() throws SQLException {
		Sistema sistema = Sistema.getUnicaInstancia();
		Conta[] contasArray = sistema.getEntityManager().find(Conta.class);
		ArrayList<Conta> contas = new ArrayList<Conta>();
		sistema.fromArrayToArrayList(contasArray, contas);
		return contas;
	}

	public static void delete(Conta conta) throws SQLException {
		Sistema sistema = Sistema.getUnicaInstancia();
		ExtratoControlador.deletarExtrato(conta.getID());
		sistema.getEntityManager().delete(conta);
	}

	public static void delete(int id) throws SQLException {
		delete(get(id));
	}

	private static void addToBuilder(StringBuilder sb, String s) {
		sb.append(String.format("%s\n", s)); // adiciona um valor a cada linha
	}

	private static void addToBuilder(StringBuilder sb, int i) {
		sb.append(String.format("%d\n", i)); // adiciona um valor a cada linha
	}

	private static void addToBuilderSemPular(StringBuilder sb, String s) {
		sb.append(String.format("%s", s)); // adiciona um valor a cada linha
	}

	private static void addToBuilderSemPular(StringBuilder sb, int i) {
		sb.append(String.format("%d", i)); // adiciona um valor a cada linha
	}

	// Retorna na ordem: contaId, reais, centavos, tipoDeConta, estiloDeConta,
	// clienteId
	public static String parseString(Conta conta) {
		if (conta == null)
			return "null";
		// StringBuilder tem a vantagem de não ficar alocando espaço para uma
		// string a cada mudança de valor
		StringBuilder sb = new StringBuilder();
		addToBuilder(sb, conta.getID());
		addToBuilder(sb, conta.getReais());
		addToBuilder(sb, conta.getCentavos());
		addToBuilder(sb,
				(conta.getTipoDeConta() == TipoConta.CORRENTE) ? "CORRENTE"
						: "POUPANCA");
		addToBuilder(sb,
				(conta.getEstiloDeConta() == TipoEstilo.SIMPLES) ? "SIMPLES"
						: "ESPECIAL");
		addToBuilderSemPular(sb, conta.getCliente().getID());
		return sb.toString();
	}

	// Retorna de todas as contas passadas na ordem: id, nome, login, senha.
	// E depois termina passando a string END
	public static String parseString(ArrayList<Conta> contas) {
		if (contas == null)
			return "null";
		// StringBuilder tem a vantagem de não ficar alocando espaço para uma
		// string a cada mudança de valor
		StringBuilder sb = new StringBuilder();
		for (Conta conta : contas) {
			addToBuilder(sb, parseString(conta));
		}
		addToBuilderSemPular(sb, "END");
		return sb.toString();
	}

	public static ArrayList<Conta> getAll(String param) throws SQLException {
		Sistema sistema = Sistema.getUnicaInstancia();
		Conta[] contasArray = sistema.getEntityManager().find(Conta.class,
				"clienteID=?", param);
		ArrayList<Conta> contas = new ArrayList<Conta>();
		sistema.fromArrayToArrayList(contasArray, contas);
		return contas;

	}

	public static String getExtratoString(Conta conta) throws IOException {
		ArrayList<Extrato> extratos = new ArrayList<Extrato>();
		extratos = ExtratoControlador.getExtratoDaConta(conta.getID());
		String extrato = new String("Extrato da conta " + conta.getID()
				+ "\n\n");
		if (extratos != null) {
			for (int i = 0; i < extratos.size(); i++) {
				// Isto não faz o que a gente quer
				// extrato += extratos.get(i).toString()+"\n";
				extrato += ExtratoControlador.getStringExtrato(extratos.get(i))
						+ "\n";
			}
		}
		extrato += "\nSeu saldo final e de ";
		if (conta.getCentavos() > 10) {
			extrato += ("R$ " + conta.getReais() + "," + conta.getCentavos());
		} else {
			extrato += ("R$ " + conta.getReais() + "," + "0" + conta
					.getCentavos());
		}
		return extrato;
	}

	public static synchronized void DescontaTaxaCorrente(Conta conta) {
		Sistema sistema = Sistema.getUnicaInstancia();
		if (conta.getEstiloDeConta() == TipoEstilo.ESPECIAL) {
			manutencao(sistema.getManutencaoEspecialCorrenteReais(), sistema
					.getManutencaoEspecialCorrenteCentavos(), conta);
		} else {
			manutencao(sistema.getManutencaoSimplesCorrenteReais(), sistema
					.getManutencaoSimplesCorrenteCentavos(), conta);
		}
		if (conta.getReais() < 0 || conta.getCentavos() < 0) {
			jurosCorrente(sistema.getJurosCorrente(), conta);
		}
	}

	public static synchronized void JurosPoupanca(Conta conta) {
		Sistema sistema = Sistema.getUnicaInstancia();
		jurosPoupanca(sistema.getJurosPoupanca(), conta);
	}
}
